<?php

/**
 * 国免优述 供应商订单 供应商SDK
 * @author hjj
 */

namespace App\Service\VendorClient\ShopgmysApi;

use App\Model\PssModel\Area;
use App\Service\Code\Normal;
use App\Service\BaseService;
use Service\AppService\IdService;
use App\Service\TaskServer\TaskWorker;

class ShopGmysClient extends BaseService {

    //供应商编号
    const SUPPLIER_CODE = 'shopgmys';

    //redis地区换成key
    const CACHE_PREFIX = 'gmys_area_code_cache';

    private $config = [];

    public function __construct($apiBody = []) {
        $this->config = $apiBody;
    }

    /**
     * 创建订单
     * @param array $params
     * @return array|string
     */
    public function createOrder($params = []) {
        $orderInfo   = $params['supplierOrder'];
        $goodsList   = $params['orderGoods'];
        $order       = $params['order'];
        $binfo       = $params['binfo'];
        $companyInfo = $params['companyInfo'];
        $order_sn    = $orderInfo['supplier_dsn'];
        //支付流水号
        $pay_sn = $order['paySerial'] ?? $orderInfo['pay_sn'];
        //订单时间
        $order_time = $orderInfo['cd'] ? date('Y-m-d H:i:s', $orderInfo['cd']) : date('Y-m-d H:i:s', time());
        //支付时间
        $pay_time = isset($order['payTime']) && !empty($order['payTime']) ? date('YmdHis', $order['payTime']) : date('YmdHis', time());
        //运费
        $deliver_money = $orderInfo['deliver_money'] ?? 0.00;
        //下单支付的时候支付信息
        $apply_extends = @json_decode($orderInfo['apply_extends'], true);
        //省市区
        $province = $orderInfo['province'];
        $city     = in_array($orderInfo['city'], ['北京市', '上海市', '天津市', '重庆市']) ? '市辖区' : $orderInfo['city'];
        $district = $orderInfo['district'];
        //获取下单商品信息
        $palceOrderGoods = [];
        $totalGoodsMoney = '0.00';
        $specIds = array_column($goodsList, 'supplier_skucode');
        $specPriceRes = $this->getPriceAndStock($specIds);
        if($specPriceRes['code'] != Normal::SUC){
            return Normal::returnCode(Normal::FAIL, "获取商品售价失败【{$specPriceRes['msg']}】");
        }
        $specPriceArr = $specPriceRes['data']['data'];
        $specPriceBySpecId = array_column($specPriceArr, null, 'specId');
        foreach ($goodsList as $key => $item) {
            //卖价
            $price = $item['price'];
            //商品购买规格数量
            $spec_num = $item['spec_num'];
            //商品购买数量
            $quantity = $item['quantity'];
            //实际购买数量(下单数量 X 起售数量)
            $buyAcount = $spec_num * $quantity;
            if(!isset($specPriceBySpecId[$item['supplier_skucode']])){
                return Normal::returnCode(Normal::FAIL, "specId：{$item['supplier_skucode']}获取价格失败");
            }
            //下单商品组
            $goodsInfo         = [
                'goodsId'  => trim($item['supplier_commodity_key']),//商品编号
                'price'    => $specPriceBySpecId[$item['supplier_skucode']]['price'],//规格单价（单位分）我们不管卖多少价 但是给你们的就必须要和从你们那里拉下来的商品售价一样
                'quantity' => $quantity,//规格数量(购买数量)
                'specId'   => trim($item['supplier_skucode']),//规格id
            ];
            $palceOrderGoods[] = $goodsInfo;
        }
        //获取省份数据
        $areaCodeInfo = $this->getAreaCodeInfo($province, $city, $district);
        if ($areaCodeInfo['code'] != Normal::SUC) {
            return Normal::returnCode(Normal::FAIL, "获取地区编码失败【{$areaCodeInfo['msg']}】");
        }
        $requestParam = [
            'buyer'      => [
                'buyerMobile' => $orderInfo['buyer_phone'],//买家电话
            ],
            'delivery'   => [
                'cityCode'              => $areaCodeInfo['data']['city_code'],//市编码
                'districtCode'          => $areaCodeInfo['data']['district_code'],//区编码
                'idCard'                => $orderInfo['buyer_idcard'],//身份证号码
                'provinceCode'          => $areaCodeInfo['data']['province_code'],//省份编码
                'receiverAddressDetail' => $orderInfo['receive_address'],//收件人详细地址
                'receiverCityName'      => $orderInfo['city'],//收件人市
                'receiverDistrictName'  => $orderInfo['district'],//收件人区
                'receiverMobile'        => $orderInfo['recveive_phone'],//收件人联系电话
                'receiverName'          => $orderInfo['buyer_realname'],//收件人姓名
                'receiverProvinceName'  => $orderInfo['province'],//收件人省
                'zipCode'               => '100000',//邮政编码  上游没使用 默认随便填写的一个  北京
            ],
            'ext'        => '',//商户扩展字段，原样返回
            'specList'   => $palceOrderGoods,//商品列表
            'notifyUrl'  => $this->config['notifyUrl'],//回调地址(开放平台必传)
            'outOrderId' => $order_sn,//商家订单号(开放平台必传)
            'payType'    => 1,//支付方式(1.余额)
            'remark'     => 1,//用户备注
        ];
        $data         = [
            'method' => __FUNCTION__,
            'data'   => $requestParam,
        ];
        $res          = $this->execute($data);
        if ($res['code'] == Normal::SUC) {
            return Normal::returnCode(Normal::SUC, "订单推送成功", [
                'thirdpart_order_sn' => $res['data']['data']['orderId']
            ]);
        }
        return $res;
    }

    /**
     * 查询商品列表
     * @param $params
     * @return array|string
     */
    public function queryGoodsList($params = []) {
        $requestParam = [
            'method'  => __FUNCTION__,
            'is_task' => false,//命令行里面不能使用多线程task
            'data'    => [
                'pageCurrent' => $params['page'] ?? 1,//当前页(默认第一页),示例值1
                'pageSize'    => $params['limit'] ?? 10,//每页条数，默认每页10条,示例值10
                'bondedWareHouseId'    => $this->config['bondedWareHouseId'],//仓库类型2-一般贸易，1-保税仓
            ],
        ];
        $result       = $this->execute($requestParam);
        if ($result['code'] != Normal::SUC) {
            return $result;
        }
        if (!isset($result['data']['data']) || empty($result['data']['data'])) {
            return Normal::returnCode(Normal::SUC, "暂无数据", [
                'total' => 0,
                'list'  => [],
            ]);
        }
        $resData  = $result['data']['data'];
        $dataPage = [];
        $classRes = $this->getCategory();
        $allClass = $classRes['data']['data'] ?? [];
        foreach ($resData['list'] as $item) {
            if (empty($item['goodsSpecVos'])) {
                continue;
            }
            foreach ($item['goodsSpecVos'] as $signItem) {
                $item['goodsSpecVos'] = $signItem;
                $thirdClassName       = '';
                if (!empty($allClass) && !empty($item['categoryIds'])) {
                    $firstClassId  = reset($item['categoryIds']);
                    $allClassByfid = array_column($allClass, null, 'cid');
                    if (isset($allClassByfid[$firstClassId])) {
                        $allSecClass      = $allClassByfid[$firstClassId]['categories'] ?? [];
                        $allSecClassBysid = array_column($allSecClass, null, 'cid');
                        $secClassId       = $item['categoryIds'][1] ?? 0;
                        if (isset($allSecClassBysid[$secClassId])) {
                            $allThClass      = $allSecClassBysid[$secClassId]['categories'] ?? [];
                            $allThClassBysid = array_column($allThClass, null, 'cid');
                            $thClassId       = $item['categoryIds'][2] ?? 0;
                            if (isset($allThClassBysid[$thClassId])) {
                                $thirdClassName = $allThClassBysid[$thClassId]['title'] ?? '';
                            }
                        }
                    }
                }
                $item['thirdClassName'] = $thirdClassName;
                $dataPage[]             = $item;
            }
        }
        // echo json_encode($dataPage, 320);die;
        return Normal::returnCode(Normal::SUC, "获取商品成功", [
            'total' => $resData['total'] ?? 0,
            'list'  => $dataPage,
        ]);
    }

    /**
     * 获取规格上下架状态，库存，价格
     */
    protected function getPriceAndStock($specIds){
        $requestParam = [
            'method'  => __FUNCTION__,
            'data'    => [
                'specIds' => $specIds
            ],
        ];
        return $this->execute($requestParam);
    }

    /**
     * 获取所有分类
     */
    public function getCategory() {
        $requestParam = [
            'method'  => __FUNCTION__,
            'is_task' => false,//命令行里面不能使用多线程task
            'data'    => [],
        ];
        return $this->execute($requestParam);
    }

    /**
     * 获取地区编码信息
     */
    protected function getAreaCodeInfo($province, $city, $district) {
        // echo json_encode($provinceCode, 320);die;
        $areaInfo = Area::whereIn('area_name', [$province, $city, $district])->pluck('area_id', 'area_name')->toArray();
        if (empty($areaInfo)) {
            return Normal::returnCode(Normal::FAIL, "省市区信息获取失败");
        }
        if (!isset($areaInfo[$province]) || !isset($areaInfo[$city]) || !isset($areaInfo[$district])) {
            return Normal::returnCode(Normal::FAIL, "未找到省市区数据，请核对");
        }
        //获取省份数据
        $provinceList = $this->getAreaCode();
        //获取province code
        if (isset($provinceList[$province])) {
            $provinceCode = $provinceList[$province];
        } else if (isset($provinceList[$province . "市"])) {
            $provinceCode = $provinceList[$province . "市"];
        } else if (isset($provinceList[$province . "省"])) {
            $provinceCode = $provinceList[$province . "省"];
        } else if (isset($provinceList[$province . "自治区"])) {
            $provinceCode = $provinceList[$province . "自治区"];
        } else {
            return Normal::returnCode(Normal::FAIL, "省份不正确，请核对！");
        }
        //获取city code
        $cityList = BaseService::instance()->getRedisObj()->hGet(self::CACHE_PREFIX, $provinceCode);
        if (empty($cityList)) {
            return Normal::returnCode(Normal::FAIL, "市信息获取失败-1");
        }
        $cityList = json_decode($cityList, true);
        $cityMap  = array_column($cityList, 'areaId', 'areaName');
        if (!isset($cityMap[$city])) {
            return Normal::returnCode(Normal::FAIL, "市信息获取失败-2");
        }
        $cityCode = $cityMap[$city];
        //获取地区
        $districtMap = array_column($cityList[$cityCode]['list'], 'areaId', 'areaName');
        //获取district code
        if (isset($districtMap[$district])) {
            $districtCode = $districtMap[$district];
            //如果是区
        } else if (isset($districtMap[$district . "区"])) {
            $districtCode = $districtMap[$district . "区"];
            //如果是县
        } else if (isset($districtMap[$district . "县"])) {
            $districtCode = $districtMap[$district . "县"];
            //如果是县升级为区
        } else if (strpos($district, "县") !== false && isset($districtMap[str_replace("县", "", $district) . "区"])) {
            $districtCode = $districtMap[str_replace("县", "", $district) . "区"];
        } else {
            return Normal::returnCode(Normal::FAIL, "地区不正确，请核对！");
        }

        $result['province_code'] = $provinceCode;
        $result['city_code']     = $cityCode;
        $result['district_code'] = $districtCode;
        return Normal::returnCode(Normal::SUC, "查询成功", $result);
    }

    /**
     * 获取地区编码
     */
    protected function getAreaCode() {
        $redis     = BaseService::instance()->getRedisObj();
        $cache_key = self::CACHE_PREFIX;
        $list      = $redis->hGet($cache_key, '0');
        if (!empty($list)) {
            return json_decode($list, true);
        }
        $regions       = $this->getArea();
        $firstAreaInfo = [];
        if (!empty($regions['data'])) {
            $list = treeKeys($regions['data'], 0, 'areaId', 'pid');
            if (!empty($list)) {
                $firstAreaInfo = array_column($list, 'areaId', 'areaName');
                $redis->hSet($cache_key, '0', json_encode($firstAreaInfo, JSON_UNESCAPED_UNICODE));
                //分散省份数据
                foreach ($list as $province_code => $data) {
                    $redis->hSet($cache_key, (string)$province_code, json_encode($data['list'], JSON_UNESCAPED_UNICODE));
                }
            }
            return $firstAreaInfo;
        }
        return [];
    }

    /**
     * 在线获取省市区编码
     */
    protected function getArea() {
        $parameter = [
            [
                'tag'    => self::SUPPLIER_CODE . '_' . __FUNCTION__,
                'uid'    => IdService::instance()->getOtherID(),
                'url'    => $this->config['getAreaCode'],
                'data'   => [],
                'method' => 'get',
                'format' => 'json',
                'header' => []
            ]
        ];
        $resTask   = TaskWorker::instance()->addTask($parameter);
        $resTask   = current($resTask);
        $response  = $resTask['response']['result'];
        $result    = @json_decode($response, true);
        // echo json_encode($result, 320);die;
        return Normal::returnCode(Normal::SUC, "获取成功", $result);
    }

    /**
     * CURL请求
     * @param $params
     * @return mixed|
     */
    public function execute($params = []) {
        $reqParam = $this->buildReqParam($params['data']);
        $signRes  = $this->generateSign($reqParam);
        if ($signRes['code'] != Normal::SUC) {
            return Normal::returnCode(Normal::FAIL, $signRes['msg']);
        }
        $sign       = $signRes['data'];
        $method     = $params['method'];
        $requestUrl = $this->config['baseUrl'] . $this->config[$method];
        $parameter  = [
            [
                'tag'    => self::SUPPLIER_CODE . '_' . $method,
                'uid'    => IdService::instance()->getOtherID(),
                'url'    => $requestUrl,
                'data'   => $reqParam,
                'method' => 'post',
                'format' => 'json',
                'header' => [
                    "Content-Type: application/json",
                    "Authorization: {$sign}",
                ],
            ],
        ];
        if (isset($params['is_task']) && $params['is_task'] === false) {
            $resTask = TaskWorker::instance()->addTask($parameter, 'handleDiableTask');
        } else {
            $resTask = TaskWorker::instance()->addTask($parameter);
        }
        $resTask = current($resTask);
        $result  = @json_decode($resTask['response']['result'], true);
        // echo json_encode($result, 320);die;
        if (isset($result['code']) && $result['success'] == 1000) {
            return Normal::returnCode(Normal::SUC, "请求成功", $result);
        }
        $msg     = $result['msg'] ?? '请求失败';
        $subCode = $result['subCode'] ?? '';
        $subMsg  = $result['subMsg'] ?? '';
        return Normal::returnCode(Normal::FAIL, $msg . "subCode：$subCode " . "subMsg：$subMsg");
    }

    /**
     * build request param
     */
    protected function buildReqParam($param) {
        $commonParam = [
            'merchId'   => $this->config['merchId'],
            'signId'    => $this->config['signId'],
            'nonceStr'  => time() . rand(1000, 99999),
            'timestamp' => date('Y-m-d H:i:s'),
        ];
        return array_merge($commonParam, $param);
    }

    /**
     * 签名计算
     * 所有接口的请求数据都是是一个json格式的字符串。假设该字符串为body, 则将原始body 直接作为签名
     * 字符串并用商户私钥进行签名，得到签名字符串signStr。注意删除signStr中的换行符，否则会导致平台方验签失败
     */
    protected function generateSign($params) {
        $params     = json_encode($params);
        $privateKey = $this->config['privateKey'];
        $privateKey = "-----BEGIN PRIVATE KEY-----\n" .
            wordwrap($privateKey, 64, "\n", true) .
            "\n-----END PRIVATE KEY-----";
        try {
            openssl_sign($params, $signature, $privateKey, OPENSSL_ALGO_SHA256);
            $sign = base64_encode($signature);
            !is_resource($privateKey) ?: openssl_free_key($privateKey);
            $sign = str_replace(PHP_EOL, "", $sign);
            //自检
            $verifyRes = $this->verifySign($sign, $params);
            if ($verifyRes['code'] != Normal::SUC) {
                return Normal::returnCode(Normal::FAIL, "加签失败【{$verifyRes['msg']}】");
            }
            return Normal::returnCode(Normal::SUC, "加签成功", $sign);
        } catch (\Throwable $th) {
            return Normal::returnCode(Normal::FAIL, "加签失败【{$th->getMessage()}】");
        }
    }

    /**
     * 验签
     */
    protected function verifySign($sign, $params) {
        $publicKey = $this->config['publicKey'];
        $publicKey = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($publicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
        // 验签
        try {
            // 如果签名正确返回 1, 签名错误返回 0, 内部发生错误则返回-1
            $result = openssl_verify($params, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
            !is_resource($publicKey) ?: openssl_free_key($publicKey);
            if ($result === 1) {
                return Normal::returnCode(Normal::SUC, "验签成功");
            }
            return Normal::returnCode(Normal::FAIL, "验签失败");
        } catch (\Throwable $e) {
            return Normal::returnCode(Normal::FAIL, "验签失败【{$e->getMessage()}】");
        }
    }

}